package com.gitee.wsl.time.wheel

import androidx.annotation.GuardedBy
import com.gitee.wsl.time.wheel.WriteOrderDeque.WriteOrder
import com.gitee.wsl.text.format.format


/**
 * An entry in the cache containing the key, value, weight, access, and write metadata. The key
 * or value may be held weakly or softly requiring identity comparison.
 *
 * @author ben.manes@gmail.com (Ben Manes)
 */
abstract class Node<K, V> : AccessOrderDeque.AccessOrder<Node<K, V>>, WriteOrder<Node<K, V>> {

    abstract val key: K?

    /**
     * Returns the reference that the cache is holding the entry by. This is either the key if
     * strongly held or a [ref.WeakReference] to that key.
     */
    abstract val keyReference: Any?

    abstract val value: V?

    /**
     * Returns the reference to the value. This is either the value if strongly held or a
     * [ref.Reference] to that value.
     */
    abstract val valueReference: Any?

    /** Sets the value, which may be held strongly, weakly, or softly.  */
    @GuardedBy("this")
    abstract fun setValue(value: V, referenceQueue: ReferenceQueue<V>?)

    /**
     * Returns `true` if the given objects are considered equivalent. A strongly held value is
     * compared by equality and a weakly or softly held value is compared by identity.
     */
    abstract fun containsValue(value: V): Boolean

    @get:GuardedBy("this")
    @set:GuardedBy("this")
    var weight: Int
        /** Returns the weight of this entry from the entry's perspective.  */
        get() = 1
        /** Sets the weight from the entry's perspective.  */
        set(weight) {}

    var policyWeight: Int
        /** Returns the weight of this entry from the policy's perspective.  */
        get() = 1
        /** Sets the weight from the policy's perspective.  */
        set(weight) {}

    /* --------------- Health --------------- */
    /** If the entry is available in the hash-table and page replacement policy.  */
    abstract val isAlive: Boolean

    @get:GuardedBy("this")
    abstract val isRetired: Boolean

    @get:GuardedBy("this")
    abstract val isDead: Boolean

    /** Sets the node to the `retired` state.  */
    @GuardedBy("this")
    abstract fun retire()

    /** Sets the node to the `dead` state.  */
    @GuardedBy("this")
    abstract fun die()

    /* --------------- Variable order --------------- */
    open var variableTime: Long
        /** Returns the variable expiration time, in nanoseconds.  */
        get() = 0L
        /**
         * Sets the variable expiration time in nanoseconds. This update may be set lazily and rely on the
         * memory fence when the lock is released.
         */
        set(time) {}

    /**
     * Atomically sets the variable time to the given updated value if the current value equals the
     * expected value and returns if the update was successful.
     */
    fun casVariableTime(expect: Long, update: Long): Boolean {
        throw UnsupportedOperationException()
    }

    open var previousInVariableOrder: Node<K,V>?
        // @GuardedBy("evictionLock")
        get() {
            throw UnsupportedOperationException()
        }
        // @GuardedBy("evictionLock")
        set(prev) {
            throw UnsupportedOperationException()
        }

    open var nextInVariableOrder: Node<K,V>?
        // @GuardedBy("evictionLock")
        get() {
            throw UnsupportedOperationException()
        }
        // @GuardedBy("evictionLock")
        set(prev) {
            throw UnsupportedOperationException()
        }

    /** Returns if the entry is in the Window or Main space.  */
    fun inWindow(): Boolean {
        return this.queueType == WINDOW
    }

    /** Returns if the entry is in the Main space's probation queue.  */
    fun inMainProbation(): Boolean {
        return this.queueType == PROBATION
    }

    /** Returns if the entry is in the Main space's protected queue.  */
    fun inMainProtected(): Boolean {
        return this.queueType == PROTECTED
    }

    /** Sets the status to the Window queue.  */
    fun makeWindow() {
        this.queueType = WINDOW
    }

    /** Sets the status to the Main space's probation queue.  */
    fun makeMainProbation() {
        this.queueType = PROBATION
    }

    /** Sets the status to the Main space's protected queue.  */
    fun makeMainProtected() {
        this.queueType = PROTECTED
    }

    var queueType: Int
        /** Returns the queue that the entry's resides in (window, probation, or protected).  */
        get() = WINDOW
        /** Set queue that the entry resides in (window, probation, or protected).  */
        set(queueType) {
            throw UnsupportedOperationException()
        }

    var accessTime: Long
        /** Returns the time that this entry was last accessed, in ns.  */
        get() = 0L
        /**
         * Sets the access time in nanoseconds. This update may be set lazily and rely on the memory fence
         * when the lock is released.
         */
        set(time) {}


    override var previousInAccessOrder: Node<K, V>?
        // @GuardedBy("evictionLock")
        get() = null
        // @GuardedBy("evictionLock")
        set(prev) {
            throw UnsupportedOperationException()
        }


    override var nextInAccessOrder: Node<K, V>?
        // @GuardedBy("evictionLock")
        get() = null
        // @GuardedBy("evictionLock")
        set(next) {
            throw UnsupportedOperationException()
        }

    /* --------------- Write order --------------- */
    var writeTime: Long
        /** Returns the time that this entry was last written, in ns.  */
        get() = 0L
        /**
         * Sets the write-time in nanoseconds. This update may be set lazily and rely on the memory fence
         * when the lock is released.
         */
        set(time) {}

    /**
     * Atomically sets the write-time to the given updated value if the current value equals the
     * expected value and returns if the update was successful.
     */
    fun casWriteTime(expect: Long, update: Long): Boolean {
        throw UnsupportedOperationException()
    }


    override var previousInWriteOrder: Node<K,V>?
        // @GuardedBy("evictionLock")
        get() = null
        // @GuardedBy("evictionLock")
        set(prev) {
            throw UnsupportedOperationException()
        }


    override var nextInWriteOrder: Node<K,V>?
        // @GuardedBy("evictionLock")
        get() = null
        // @GuardedBy("evictionLock")
        set(next) {
            throw UnsupportedOperationException()
        }

    override fun toString(): String {
        return String.format(
             ("%s=[key=%s, value=%s, weight=%d, queueType=%,d, accessTimeNS=%,d, "
                    + "writeTimeNS=%,d, varTimeNs=%,d, prevInAccess=%s, nextInAccess=%s, prevInWrite=%s, "
                    + "nextInWrite=%s]"), this::class.simpleName, this.key, this.value, this.weight,
            this.queueType, this.accessTime, this.writeTime, this.variableTime,
            this.previousInAccessOrder != null, this.nextInAccessOrder != null,
            previousInWriteOrder != null, nextInWriteOrder != null
        )
    }

    companion object {
        /* --------------- Access order --------------- */
        const val WINDOW: Int = 0
        const val PROBATION: Int = 1
        const val PROTECTED: Int = 2
    }
}
